﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Boggle;
using NUnit.Framework;

namespace BoggleTests
{
    //unit test structure inspired by Phil Haack http://haacked.com/archive/2012/01/01/structuring-unit-tests.aspx
    [TestFixture]
    public class BoardTests
    {
        /// <summary>
        /// y o x
        /// r b a
        /// v e d
        /// </summary>
        private static readonly List<string> BungieSampleLetters = new List<string> { "y", "o", "x", "r", "b", "a", "v", "e", "d" };

        public class TheConstructor
        {
            [Test]
            public void HeightIsSet()
            {
                int height = 1;
                int width = 1;
                List<string> letters = new List<string> { "a" };
                BoggleBoard board = new BoggleBoard(1, 1, letters);
                Assert.That(board.Height, Is.EqualTo(height));
            }

            [Test]
            public void WidthIsSet()
            {
                int height = 1;
                int width = 1;
                List<string> letters = new List<string> { "a" };
                BoggleBoard board = new BoggleBoard(1, 1, letters);
                Assert.That(board.Width, Is.EqualTo(width));
            }

            [TestCase(-1, 1, new string[] { "a" }, TestName = "negative height", ExpectedException = typeof(ArgumentException), ExpectedMessage = "height must be greater than 0", MatchType = MessageMatch.Contains)]
            [TestCase(1, -1, new string[] { "a" }, TestName = "negative width", ExpectedException = typeof(ArgumentException), ExpectedMessage = "width must be greater than 0", MatchType = MessageMatch.Contains)]
            [TestCase(1, 1, null, TestName = "null letters", ExpectedException = typeof(ArgumentNullException), ExpectedMessage = "letters", MatchType = MessageMatch.Contains)]
            [TestCase(1, 1, new string[] { }, TestName = "empty letters", ExpectedException = typeof(ArgumentException), ExpectedMessage = "letters cannot be empty", MatchType = MessageMatch.Contains)]
            [TestCase(3, 3, new string[] { "a" }, TestName = "letters and board size mismatch", ExpectedException = typeof(ArgumentException), ExpectedMessage = "letters do not match the dimension of the boggle board", MatchType = MessageMatch.Contains)
            ]
            public void ThrowsExceptionOnInvalidInputs(int height, int width, IEnumerable<string> letters)
            {
                BoggleBoard board = new BoggleBoard(height, width, letters);
            }

            [TestCase("y", 0, 0)]
            [TestCase("o", 0, 1)]
            [TestCase("x", 0, 2)]   
            [TestCase("r", 1, 0)]
            [TestCase("b", 1, 1)]
            [TestCase("a", 1, 2)]
            [TestCase("v", 2, 0)]
            [TestCase("e", 2, 1)]
            [TestCase("d", 2, 2)]
            public void GridIsCreated(string letter, int row, int column)
            {
                BoggleBoard board = new BoggleBoard(3, 3, BungieSampleLetters);
                BoggleCell actualCell = board[row, column];
                BoggleCell expectedCell = new BoggleCell(letter, row, column);
                Assert.That(actualCell, Is.EqualTo(expectedCell));
            }
        }
        
        public class TheGetNeighborsMethod
        {
            /// <summary>
            /// Each position uniquely and exhuastively tests the return of the GetNeighbors method (positions are based on a 3x3 board).
            /// </summary>
            //expectedNeighboringLetters are arranged by top-left, top, top-right, right, bottom-right, bottom, bottom-left, and then left order for convenience
            [TestCase(0, 0, new string[] { "o", "b", "r" }, TestName = "Position [0,0]")]
            [TestCase(0, 1, new string[] { "x", "a", "b", "r", "y" }, TestName = "Position [0,1]")]
            [TestCase(0, 2, new string[] { "a", "b", "o" }, TestName = "Position [0,2]")]
            [TestCase(1, 0, new string[] { "y", "o", "b", "e", "v" }, TestName = "Position [1,0]")]
            [TestCase(1, 1, new string[] { "y", "o", "x", "a", "d", "e", "v", "r" }, TestName = "Position [1,1]")]
            [TestCase(1, 2, new string[] { "o", "x", "d", "e", "b" }, TestName = "Position [1,2]")]
            [TestCase(2, 0, new string[] { "r", "b", "e" }, TestName = "Position [2,0]")]
            [TestCase(2, 1, new string[] { "r", "b", "a", "d", "v" }, TestName = "Position [2,1]")]
            [TestCase(2, 2, new string[] { "b", "a", "e" }, TestName = "Position [2,2]")]
            public void ReturnsNeighboringCells(int row, int column, IEnumerable<string> expectedNeighboringLetters)
            {
                BoggleBoard board = new BoggleBoard(3, 3, BungieSampleLetters);
                IEnumerable<BoggleCell> neighboringCells = board.GetNeighbors(row, column);
                IEnumerable<string> neighboringLetters = neighboringCells.Select(neighboringCell => neighboringCell.Letter);
                Assert.That(neighboringLetters, Is.EquivalentTo(expectedNeighboringLetters));
            }
        }
    }
}
